/* * Cobertura - http://cobertura.sourceforge.net/ * * Copyright (C) 2008 John Lewis * Copyright (C) 2006 Mark Doliner * * Note: This file is dual licensed under the GPL and the Apache * Source License 1.1 (so that it can be used from both the main * Cobertura classes and the ant tasks). * * Cobertura is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published * by the Free Software Foundation; either version 2 of the License, * or (at your option) any later version. * * Cobertura is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Cobertura; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * USA */ package net.sourceforge.cobertura.ant; import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.FilenameFilter; import java.io.IOException; import java.util.Arrays; import java.util.Iterator; import java.util.List; import junit.framework.TestCase; import net.sourceforge.cobertura.reporting.JUnitXMLHelper; import org.apache.tools.ant.Project; import org.apache.tools.ant.taskdefs.Java; import org.apache.tools.ant.types.Path; import org.apache.tools.ant.types.Path.PathElement; import org.jdom.Document; import org.jdom.Element; import org.jdom.JDOMException; import org.jdom.xpath.XPath; /** * These tests generally exec ant to run a test.xml file. A different target is used for * each test. The text.xml file sets up a test, instruments, runs junit, and generates a * coverage xml report. Then the xml report is parsed and checked. * * @author jwlewi */ public class FunctionalTest extends TestCase { private static int forkedJVMDebugPort = 0; private final static File BASEDIR = new File((System.getProperty("basedir") != null) ? System .getProperty("basedir") : ".", "examples/functionaltest1"); public static void testInstrumentUsingDirSet() throws Exception { runTestAntScript("dirset", "test-dirset"); verify("dirset"); } public static void testInstrumentUsingIncludesAndExcludes() throws Exception { runTestAntScript("includes-and-excludes", "test-includes-and-excludes"); verify("includes-and-excludes"); } public static void testInstrumentUsingClassPath() throws Exception { runTestAntScript("classpath", "test-classpath"); verify("classpath"); } public static void testInstrumentUsingWar() throws Exception { runTestAntScript("classpath", "test-war"); verify("war"); } private static void verify(String testName) throws Exception { verifyXml(testName); verifyHtml(testName); } private static void verifyXml(String testName) throws Exception { Document document = getSummaryXmlReportDocument(); verifyOverallComplexity(document); document = getXmlReportDocument(); verifyOverallComplexity(document); // Get a list of all classes listed in the XML report List classesList = getClassElements(document); assertTrue("Test " + testName + ": Did not find any classes listed in the XML report.", classesList.size() > 0); // text.xml only instruments the two "A" classes, so make // sure those are the only classes listed in the XML report. boolean firstPackageFound = false; boolean secondPackageFound = false; for (Iterator iter = classesList.iterator(); iter.hasNext();) { boolean verify = true; Element classElement = (Element)iter.next(); String className = classElement.getAttributeValue("name"); if (className.equals("test.first.A")) { firstPackageFound = true; } else if (className.equals("test.second.A")) { secondPackageFound = true; } else if (className.equals("test.first.RemoteInterface") || (className.equals("test.first.RemoteListener")) || (className.equals("test.first.RemoteListener_Stub"))) { //just ignore - it is ok verify = false; } else fail("Test " + testName + ": Found a class with the name '" + className + "' in the XML report, but was only expecting either 'test.first.A' or 'test.second.A'."); if (verify) { verifyClass(className, testName, classElement); } } assertTrue("Test " + testName + ": Did not find class 'test.first.A' in the XML report.", firstPackageFound); assertTrue("Test " + testName + ": Did not find class 'test.second.A' in the XML report.", secondPackageFound); } private static void verifyOverallComplexity(Document document) { String complexity = document.getRootElement().getAttributeValue("complexity"); assertEquals("Invalid overall complexity ", "1.0", complexity); } private static Document getXmlReportDocument() throws IOException, JDOMException { File xmlFile = new File(BASEDIR, "reports/cobertura-xml/coverage.xml"); Document document = JUnitXMLHelper.readXmlFile(xmlFile, true); return document; } private static Document getSummaryXmlReportDocument() throws IOException, JDOMException { File xmlFile = new File(BASEDIR, "reports/cobertura-xml/coverage-summary.xml"); Document document = JUnitXMLHelper.readXmlFile(xmlFile, true); return document; } /** * Use XPath to get all <class> elements in the * cobertura.xml file under the given directory. * @return A list of JDOM Elements. */ private static List getClassElements(Document document) throws IOException, JDOMException { XPath xpath = XPath.newInstance("/coverage/packages/package/classes/class"); List classesList = xpath.selectNodes(document); return classesList; } /** * Verify that the class's expected methods are found. Look for * a method called "call" which should have a hit count of 1. * The method called "dontCall" should have a hit count of 0. * @param testName */ private static void verifyClass(String className, String testName, Element classElement) { verifyComplexity(className, classElement); // Get a list of methods Element methodsElement = classElement.getChild("methods"); List methodList = methodsElement.getChildren("method"); assertTrue("Test " + testName + ": Did not find any methods listed in the class " + classElement.getAttributeValue("name"), methodList.size() > 0); boolean callMethodFound = false; boolean dontCallMethodFound = false; for (Iterator iter = methodList.iterator(); iter.hasNext();) { Element methodElement = (Element)iter.next(); String methodName = methodElement.getAttributeValue("name"); if (methodName.equals("call")) { if (callMethodFound) { fail("Test " + testName + ": Found more than one instance of the method 'call' in the class " + classElement.getAttributeValue("name")); } callMethodFound = true; verifyMethod(testName, classElement, methodElement, 1); } else if (methodName.equals("dontCall")) { if (dontCallMethodFound) { fail("Test " + testName + ": Found more than one instance of the method 'dontCall' in the class " + classElement.getAttributeValue("name")); } dontCallMethodFound = true; verifyMethod(testName, classElement, methodElement, 0); } else if (methodName.equals("<init>") || methodName.equals("someMethod")) { // These methods are ok--ignore them. } else { fail("Test " + testName + ": Found method " + methodName + " in the class " + classElement.getAttributeValue("name") + ", but was only expecting either 'call' or 'dontCall'."); } } assertTrue("Test " + testName + ": Did not find method 'call' in the class " + classElement.getAttributeValue("name"), callMethodFound); assertTrue("Test " + testName + ": Did not find method 'dontCall' in the class " + classElement.getAttributeValue("name"), dontCallMethodFound); } private static void verifyComplexity(String className, Element classElement) { String complexity = classElement.getAttributeValue("complexity"); assertEquals("Invalid complexity with class " + className, "1.0", complexity); } /** * Look at all lines in a method and make sure they have hit counts that * match the expectedHits. */ private static void verifyMethod(String testName, Element classElement, Element methodElement, int expectedHits) { Element linesElement = methodElement.getChild("lines"); List lineList = linesElement.getChildren("line"); assertTrue("Test " + testName + ", class " + classElement.getAttributeValue("name") + ": Did not find any lines in the method " + methodElement.getAttributeValue("name"), lineList.size() > 0); for (Iterator iter = lineList.iterator(); iter.hasNext();) { Element lineElement = (Element)iter.next(); String hitsString = lineElement.getAttributeValue("hits"); int hits = Integer.parseInt(hitsString); assertEquals("Test " + testName + ", class " + classElement.getAttributeValue("name") + ": Found incorrect hit count for the method " + methodElement.getAttributeValue("name"), expectedHits, hits); } } private static void verifyHtml(String testName) throws Exception { File htmlReportDir = new File(BASEDIR, "reports/cobertura-html"); // Get all files from report directory String htmlFiles[] = htmlReportDir.list(new FilenameFilter() { public boolean accept(File dir, String name) { return name.endsWith(".html"); } }); Arrays.sort(htmlFiles); assertTrue(htmlFiles.length >= 5); // Assert that all required files are there String[] requiredFiles = { "index.html", "help.html", "frame-packages.html", "frame-summary.html", "frame-sourcefiles.html" , "test.first.A.html"}; for (int i = 0; i < requiredFiles.length; i++) { if (!containsFile(htmlFiles, requiredFiles[i])) { fail("Test " + testName + ": File " + requiredFiles[i] + " not found among report files"); } } // Validate selected files String previousPrefix = "NONE"; for (int i = 0; i < htmlFiles.length; i++) { // Validate file if has prefix different than previous one, or is required file if (containsFile(requiredFiles, htmlFiles[i]) || !htmlFiles[i].startsWith(previousPrefix)) { JUnitXMLHelper.readXmlFile(new File(htmlReportDir, htmlFiles[i]), true); } if (htmlFiles[i].length() > 7) { previousPrefix = htmlFiles[i].substring(0, 7); } else { previousPrefix = htmlFiles[i]; } } BufferedReader reader = new BufferedReader(new FileReader(new File(htmlReportDir, "test.first.A.html"))); String line; boolean foundSomeMethod = false; while ((line = reader.readLine()) != null) { if (line.matches(".*someMethod.*")) { foundSomeMethod = true; } } assertTrue("someMethod not found in test.first.A.html", foundSomeMethod); } private static boolean containsFile(String[] files, String fileName) { for (int i = 0; i < files.length; i++) { if (files[i].equals(fileName)) return true; } return false; } /** * Use the ant 'java' task to run the test.xml * file and the specified target. */ private static void runTestAntScript(String testName, String target) throws IOException { Java task = new Java(); task.setTaskName("java"); task.setProject(new Project()); task.init(); // Call ant launcher. Requires ant-lancher.jar. task.setClassname("org.apache.tools.ant.launch.Launcher"); task.setFork(true); AntUtil.transferCoberturaDataFileProperty(task); if (forkedJVMDebugPort > 0) { task.createJvmarg().setValue("-Xdebug"); task.createJvmarg().setValue("-Xrunjdwp:transport=dt_socket,address=" + forkedJVMDebugPort + ",server=y,suspend=y"); } task.createArg().setValue("-f"); task.createArg().setValue(BASEDIR + "/build.xml"); task.createArg().setValue(target); task.setFailonerror(true); // Set output to go to a temp file File outputFile = Util.createTemporaryTextFile("cobertura-test"); task.setOutput(outputFile); // Set the classpath to the same classpath as this JVM Path classpath = task.createClasspath(); PathElement pathElement = classpath.createPathElement(); pathElement.setPath(System.getProperty("java.class.path")); try { task.execute(); } finally { if (outputFile.exists()) { // Put the contents of the output file in the exception System.out.println("\n\n\nOutput from Ant for " + testName + " test:\n----------------------------------------\n" + Util.getText(outputFile) + "----------------------------------------"); outputFile.delete(); } } } }